﻿#region License

// Another free, open solution by folks who get it.
// Copyright © 2011 Business in Unison, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using Patterns.Mapping;
using Patterns.SafeCode;

namespace Patterns.Collections
{
	/// <summary>
	/// 	Provides ease-of-use extensions to dictionary types.
	/// </summary>
	public static class DictionaryExtensions
	{
		/// <summary>
		/// 	Converts the object into a property bag, or dictionary of property names and values.
		/// </summary>
		/// <param name = "instance">The instance.</param>
		/// <returns></returns>
		public static IDictionary<string, object> AsPropertyBag(this object instance)
		{
			return instance.GetType().FindProperties()
				.ToDictionary(property => property.Name, property => property.PropertyType == typeof (string) || property.PropertyType.IsValueType
					? Try.Get(() => property.GetValue(instance, null))
					: Try.Get(() => property.GetValue(instance, null).AsPropertyBag()),
					StringComparer.OrdinalIgnoreCase);
		}

		/// <summary>
		/// 	Gets the value at the specified key.
		/// </summary>
		/// <param name = "items">The items.</param>
		/// <param name = "key">The key.</param>
		/// <param name = "initializer">The initializer.</param>
		/// <param name = "errorHandler">The error handler.</param>
		/// <returns></returns>
		public static object GetValue(this IDictionary items, object key, Func<object> initializer = null,
			Action<Exception> errorHandler = null)
		{
			errorHandler = errorHandler ?? Try.HandleErrors.DefaultStrategy;

			bool exists = Try.Get(() => items.Contains(key));

			if (!exists && initializer != null)
			{
				exists = true;
				Try.Do(() => items[key] = initializer(), error =>
				{
					exists = false;
					errorHandler(error);
				});
			}

			return exists ? items[key] : null;
		}

		/// <summary>
		/// 	Gets the value at the specified key.
		/// </summary>
		/// <typeparam name = "Key">The type of the key.</typeparam>
		/// <typeparam name = "Value">The type of the value.</typeparam>
		/// <param name = "items">The items.</param>
		/// <param name = "key">The key.</param>
		/// <param name = "initializer">The initializer.</param>
		/// <param name = "errorHandler">The error handler.</param>
		/// <returns></returns>
		public static Value GetValue<Key, Value>(this IDictionary<Key, Value> items, Key key, Func<Value> initializer = null,
			Action<Exception> errorHandler = null)
		{
			var nonGenericItems = items as IDictionary;
			Func<object> valueFactory = () => initializer == null ? default(Value) : initializer();
			return (Value) nonGenericItems.GetValue(key, valueFactory, errorHandler);
		}
	}
}